package xj.graph2d.shapes.uml;

import static xj.graph2d.DrawShapeConstants.cpady;
import static xj.graph2d.template.UMLClassDiagramTemplate.ABSTRACT_CLASSNAME_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.ABSTRACT_CLASS_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.ABSTRACT_OPERATION_COLOR;
import static xj.graph2d.template.UMLClassDiagramTemplate.ABSTRACT_OPERATION_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.ATTRIBUTE_COLOR;
import static xj.graph2d.template.UMLClassDiagramTemplate.ATTRIBUTE_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.ATTRIBUTE_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.CLASS_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.CLASS_NAME_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.INTERFACE_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.INTERFACE_NAME_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.OPERATION_COLOR;
import static xj.graph2d.template.UMLClassDiagramTemplate.OPERATION_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.OPERATION_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PRIVATE_ATTRIBUTE_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PRIVATE_OPERATION_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PROTECTED_ATTRIBUTE_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PROTECTED_OPERATION_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PUBLIC_ATTRIBUTE_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.PUBLIC_OPERATION_ICON;
import static xj.graph2d.template.UMLClassDiagramTemplate.STATIC_ATTRIBUTE_COLOR;
import static xj.graph2d.template.UMLClassDiagramTemplate.STATIC_ATTRIBUTE_FONT;
import static xj.graph2d.template.UMLClassDiagramTemplate.STATIC_OPERATION_COLOR;
import static xj.graph2d.template.UMLClassDiagramTemplate.STATIC_OPERATION_FONT;
import static xj.graph2d.template.VisualClassTemplate.ATTR_BG;
import static xj.graph2d.template.VisualClassTemplate.ATTR_FG;
import static xj.graph2d.template.VisualClassTemplate.ENUM_BG;
import static xj.graph2d.template.VisualClassTemplate.ENUM_FG;
import static xj.graph2d.template.VisualClassTemplate.OPER_BG;
import static xj.graph2d.template.VisualClassTemplate.OPER_FG;

import java.awt.Font;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.Icon;

import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.FontAttrEx;
import xj.graph2d.Label;
import xj.graph2d.RectangleShape;
import xj.graph2d.TextShape;
import xj.graph2d.shapes.BoxLabel;
import xj.graph2d.shapes.CompartmentalRectangleShape;
import xj.graph2d.shapes.IconShape;
import xj.graph2d.shapes.block.BlockShape;
import xj.graph2d.shapes.block.SimpleStatementShape;
import xj.graph2d.shapes.block.StatementShape;
import xj.graph2d.template.UMLClassDiagramTemplate;
import xj.meta.ModelElement;
import xj.model.uml.Attribute;
import xj.model.uml.Enumeration;
import xj.model.uml.Operation;
import xj.model.uml.UMLClass;
import xj.model.uml.Value;
import xj.model.uml.Visibility;

public class ClassShape 
  extends CompartmentalRectangleShape {

  public ClassShape() {
    super();
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    initDrawAttr();
  }

  public ClassShape(boolean createComp) {
    super(createComp);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    initDrawAttr();
  }

  public ClassShape(float[] dividers, boolean filled, boolean createComp) {
    super(dividers, filled, createComp);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    if (dividers != null && 
	dividers.length >= 1) {
      attrOperCompartmentShown = true;
    } else {
      attrOperCompartmentShown = false;
    }
    initDrawAttr();
  }

  public ClassShape(float x1, float y1, float x2, float y2) {
    this(x1, y1, x2, y2, null);
  }

  public ClassShape(float x1, float y1, float x2, float y2, float[] dividers) {
    super(x1, y1, x2, y2, dividers);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    if (dividers != null && 
	dividers.length >= 1) {
      attrOperCompartmentShown = true;
    } else {
      attrOperCompartmentShown = false;
    }
    initDrawAttr();
  }

  protected void initDrawAttr() { 
    setFilled(true);
    attrClass = new DrawAttr();
    attrActiveClass = new DrawAttr();
    attrMixinClass = new DrawAttr();
    attrAbstractClass = new DrawAttr();
    attrInterface = new DrawAttr();
    attrEnum = new DrawAttr();
    attrException = new DrawAttr();
    setDrawAttr();
  }

  protected void setDrawAttr() { 
    if (_interface) {
      attr = attrInterface; 
    } else if (_abstract) { 
      attr = attrAbstractClass; 
    } else if (_active) { 
      attr = attrActiveClass;
    } else if (_mixin) { 
      attr = attrMixinClass;
    } else if (_enum) { 
      attr = attrEnum;
    } else if (_exception) { 
      attr = attrException;
    } else { 
      attr = attrClass;
    }    
  }

  @Override
  public String getDisplayName() {
    if (_interface) {
      return "Interface";
    } else if (_abstract) { 
      return "Abstract class";
    } else if (_active) { 
      return "Active class";
    } else if (_mixin) { 
      return "MixIn";
    } else if (_enum) { 
      return "Enumeration";
    } else if (_exception) { 
      return "Exception";
    } else { 
      return "Class";
    }    
  }

  @Override
  public DrawShape makeInstance() {
    return new ClassShape(null, filled, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    super.copyTo(s);
    if (s instanceof ClassShape) {
      ClassShape cs = (ClassShape) s;
      cs._interface = _interface;
      cs._abstract = _abstract;
      cs._active = _active;
      cs._mixin = _mixin;
      cs._enum = _enum;
      cs._exception = _exception;
      cs.attrOperCompartmentShown = attrOperCompartmentShown;
      cs.hasAttribute = hasAttribute;

      if (attrClass != null) {
	cs.attrClass = (DrawAttr) attrClass.clone();
      }
      if (attrInterface != null) {
	cs.attrInterface = (DrawAttr) attrInterface.clone();
      }
      if (attrAbstractClass != null) {
	cs.attrAbstractClass = (DrawAttr) attrAbstractClass.clone();
      }
      if (attrActiveClass != null) {
	cs.attrActiveClass = (DrawAttr) attrActiveClass.clone();
      }
      if (attrMixinClass != null) {
	cs.attrMixinClass = (DrawAttr) attrMixinClass.clone();
      }
      if (attrEnum != null) {
	cs.attrEnum = (DrawAttr) attrEnum.clone();
      }
      if (attrException != null) {
	cs.attrException = (DrawAttr) attrException.clone();
      }

      if (cs._interface) {
	cs.attr = cs.attrInterface; 
      } else if (cs._abstract) { 
	cs.attr = cs.attrAbstractClass; 
      } else if (cs._active) { 
	cs.attr = cs.attrActiveClass;
      } else if (cs._mixin) { 
	cs.attr = cs.attrMixinClass;
      } else if (cs._enum) { 
	cs.attr = cs.attrEnum;
      } else if (cs._exception) { 
	cs.attr = cs.attrException;
      } else { 
	cs.attr = cs.attrClass;
      }
    }
  }

  @Override
  protected void cloneSubshapes(DrawShapeBase s, boolean deepClone) 
    throws CloneNotSupportedException {
    super.cloneSubshapes(s, deepClone);
    if (s instanceof ClassShape) {
      ClassShape cs = (ClassShape) s;
      if (hasAttribute) { 
	cs.attrBox = cs.getShapeInCompartment(1);
	cs.operBox = cs.getShapeInCompartment(2);      
      } else { 
	cs.attrBox = null;
	cs.operBox = cs.getShapeInCompartment(1);      
      }
    }
  }

  @Override
  public String getNodeDisplayName() { 
    return getClassName();
  }

  public String getClassName() {
    return UMLUtil.getName(this);
  }

  public void setClassName(String cname) {
    setClassName(cname, false, false);
  }

  public void setClassName(String cname, boolean isInterface, boolean isAbstract) {
    UMLUtil.setName(this, cname);
    setInterface(isInterface);
    setAbstract(isAbstract);
    Label label = UMLUtil.getNameShape(this);
    if (label != null) {
      TextShape text = null;
      if (label instanceof BoxLabel) {
	BoxLabel blabel = (BoxLabel) label;
	text = blabel.getTextShape();
	if (isInterface) {
	  if (interfaceIcon != null) {
	    blabel.setIcon(interfaceIcon, INTERFACE_ICON);
	  }
	} else if (isAbstract) {
	  if (absClassIcon != null) {
	    blabel.setIcon(absClassIcon, ABSTRACT_CLASS_ICON);
	  }
	} else {
	  if (classIcon != null) {
	    blabel.setIcon(classIcon, CLASS_ICON);
	  }
	}
	if (isInterface || isAbstract) {
	  blabel.setTextFontStyle(Font.BOLD | Font.ITALIC);
	} else {
	  blabel.setTextFontStyle(Font.BOLD);
	}
      } else if (label instanceof TextShape) {
	text = (TextShape) label;
	DrawAttr attr = text.getDrawAttr(); 
	if (isInterface || isAbstract) {
	  attr.setFontStyle(Font.BOLD | Font.ITALIC);
	} else { 
	  attr.setFontStyle(Font.BOLD);
	}
      }      
      doLayout();
    }
  }

  public String getClassStereotypes() {
    return UMLUtil.getStereotypes(this);
  }

  public void setClassStereotypes(String st) {
    UMLUtil.setStereotypes(this, st);
    fitCompartments();
    doLayout();
  }

  public String getClassPackage() {
    return UMLUtil.getPackage(this);
  }

  public void setClassPackage(String pkgname) {
    UMLUtil.setPackage(this, pkgname);
    fitCompartments();
    doLayout();
  }

  public String getTemplates() {
    DrawShape tshape = findTemplateShape();
    if (tshape instanceof RectangleShape) {
      RectangleShape rect = (RectangleShape) tshape;
      TextShape text = (TextShape) rect.getLabelShape();
      String s = text.getText();
      if (s != null) {
	return s.trim();
      }
    }
    return null;
  }

  public void setTemplates(String temp) {
    setTemplates(temp, false);
  }

  public void setTemplates(String temp, boolean fit) {
    TextShape text = null;
    DrawShape tshape = findTemplateShape();
    if (tshape instanceof RectangleShape) {
      RectangleShape rect = (RectangleShape) tshape;
      text = (TextShape) rect.getLabelShape();
      if (text != null) {
	text.setText(temp);
	//text.doLayout();
	
	if (fit) { 
	  rect.adjustContent(false);
	}
	rect.doLayout();

	tshape.setVisible(true);
      }
      if (temp == null || temp.length() == 0) {
	//removeShape(tshape);
	text.setText(null);
	tshape.setVisible(false);
      }

      doLayout();
    }
  }

  public List<Attribute> getAttributes() {
    if (!isEnum() && 
	attrBox != null) {
      List<Attribute> result = new ArrayList<Attribute>();
      if (attrBox instanceof BoxLabel) { 
	BoxLabel box = (BoxLabel) attrBox;
	Iterator iter = box.allShapes();
	while (iter.hasNext()) {
	  Object o = iter.next();
	  if (o instanceof Label) {
	    String attr = ((Label) o).getText();
	    if (attr != null && attr.length() > 0) {
	      Attribute f = null;
	      ModelElement model = ((DrawShape) o).getModel();
	      if (model instanceof Attribute) {
		f = (Attribute) model;
	      }
	      if (f == null) {
		f = Attribute.makeAttribute(attr);
	      } else {
		f = Attribute.makeAttribute(f, attr);
	      }
	      if (f != null) {
		result.add(f);
	      }
	    }

	  } else if (o instanceof TextShape) {
	    
	  }
	}
      } else if (attrBox instanceof BlockShape) {
	BlockShape block = (BlockShape) attrBox; 
	List<StatementShape> slist = block.getAllStatements();
	if (slist != null) { 
	  for (int i = 0; i < slist.size(); i++) {
	    StatementShape s = slist.get(i);
	    if (s instanceof SimpleStatementShape) { 
	      SimpleStatementShape s0 = (SimpleStatementShape) s;
	      String attr = s0.getText();
	      if (attr != null && attr.length() > 0) {
		Attribute f = null;		
		ModelElement model = s0.getModel();
		if (model instanceof Attribute) {
		  f = (Attribute) model;
		}
		if (f == null) { 
		  f = Attribute.makeAttribute(attr);
		}
		if (f != null) {
		  result.add(f);
		}
	      }
	    }
	  }
	}
      }
      return result;
    }
    return null;
  }

  public List<Value> getEnumValues() {
    if (isEnum() && 
	attrBox != null) {
      List<Value> result = new ArrayList<Value>();
      if (attrBox instanceof BoxLabel) { 
	BoxLabel box = (BoxLabel) attrBox;
	Iterator iter = box.allShapes();
	while (iter.hasNext()) {
	  Object o = iter.next();
	  if (o instanceof Label) {
	    String attr = ((Label) o).getText();
	    if (attr != null && attr.length() > 0) {
	      Value v = new Value(attr);
	      result.add(v);
	    }
	  } else if (o instanceof TextShape) {
	    String attr = ((TextShape) o).getText();
	    if (attr != null && attr.length() > 0) {
	      Value v = new Value(attr);
	      result.add(v);
	    }
	  }
	}
      } else if (attrBox instanceof BlockShape) {
	BlockShape block = (BlockShape) attrBox; 
	List<StatementShape> slist = block.getAllStatements();
	if (slist != null) { 
	  for (int i = 0; i < slist.size(); i++) {
	    StatementShape s = slist.get(i);
	    if (s instanceof SimpleStatementShape) { 
	      SimpleStatementShape s0 = (SimpleStatementShape) s;
	      String attr = s0.getText();
	      if (attr != null && attr.length() > 0) {		
		Value v = null;
		ModelElement model = s0.getModel();
		if (model instanceof Value) { 
		  v = (Value) model;
		}
		if (v == null) { 
		  v = new Value(attr);
		}
		result.add(v);
	      }
	    }
	  }
	}
      }
      return result;
    }
    return null;
  }

  public List<Operation> getOperations() {
    if (operBox != null) {
      List<Operation> result = new ArrayList<Operation>();
      if (operBox instanceof BoxLabel) { 
	BoxLabel box = (BoxLabel) operBox;
	Iterator iter = box.allShapes();
	while (iter.hasNext()) {
	  Object o = iter.next();
	  if (o instanceof Label) {
	    String oper = ((Label) o).getText();
	    if (oper != null && oper.length() > 0) {
	      Operation m = null;
	      ModelElement model = ((DrawShape) o).getModel();
	      if (model instanceof Operation) {
		m = (Operation) model;
	      }
	      if (m == null) {
		m = Operation.makeOperation(oper);
	      } else {
		m = Operation.makeOperation(m, oper);
	      }
	      if (m != null) {
		result.add(m);
	      }
	    }
	    
	  } else if (o instanceof TextShape) {
	    
	  }
	}
      } else if (operBox instanceof BlockShape) {
	BlockShape block = (BlockShape) operBox; 
	List<StatementShape> slist = block.getAllStatements();
	if (slist != null) { 
	  for (int i = 0; i < slist.size(); i++) {
	    StatementShape s = slist.get(i);
	    if (s instanceof SimpleStatementShape) { 
	      SimpleStatementShape s0 = (SimpleStatementShape) s;
	      String oper = s0.getText();
	      if (oper != null && oper.length() > 0) {
		Operation m = null;
		ModelElement model = s0.getModel();
		if (model instanceof Operation) {
		  m = (Operation) model;
		}
		if (m == null) { 
		  m = Operation.makeOperation(oper);
		}
		if (m != null) {
		  result.add(m);
		}
	      }
	    }
	  }
	}
      }
      return result;
    }
    return null;
  }

  public void setAttributes(List<Attribute> attributes) {
    if (!isEnum() && 
       attrBox != null) {
      if (attrBox instanceof BoxLabel) { 
	TextShape text = null;
	attrBox.removeAllShapes();
	
	if (attributes != null && attributes.size() > 0) {
	  Iterator<Attribute> iter = attributes.iterator();
	  while (iter.hasNext()) {
	    Attribute f = iter.next();
	    
	    String fontKey = ATTRIBUTE_FONT;
	    String colorKey = ATTRIBUTE_COLOR;
	    if (f.isStatic()) {
	      fontKey = STATIC_ATTRIBUTE_FONT;
	      colorKey = STATIC_ATTRIBUTE_COLOR;
	    }
	    
	    text = template.makeContentText(f.toString(true, true), "attribute",
					    true, // multiline
					    fontKey, colorKey);
	    text.setDisplayName("Attribute");
	    Icon icon = null;
	    String iconName = null;
	    Visibility v = f.getVisibility();
	    if (v != null && 
		template.useIconInLabel()) {
	      switch (v) {
	      case Public:
		icon = publicAttributeIcon;
		iconName = PUBLIC_ATTRIBUTE_ICON;
		break;
	      case Protected:
		icon = protectedAttributeIcon;
		iconName = PROTECTED_ATTRIBUTE_ICON;
		break;
	      case Private:
		icon = privateAttributeIcon;
		iconName = PRIVATE_ATTRIBUTE_ICON;
		break;
	      case Package:
	      default:
		icon = defaultAttributeIcon;
		iconName = ATTRIBUTE_ICON;
		break;
	      }
	      BoxLabel flabel = new BoxLabel(new IconShape(icon, iconName), text);
	      
	      flabel.setDetachable(false);
	      flabel.setScalable(false);
	      flabel.setMovable(false);
	      flabel.setModel(f);
	      attrBox.addShape(flabel);
	    } else {
	      text.setModel(f);
	      attrBox.addShape(text);
	    }
	  }
	  if (attrBox instanceof BoxLabel) { 
	    BoxLabel box = (BoxLabel) attrBox; 
	    box.linkTextLines();
	  }
	} else {
	  // no attribute
	  text = template.makeContentText(null, "attribute", 
					  true, // multiline
					  ATTRIBUTE_FONT,
					  ATTRIBUTE_COLOR);
	  if (template.useIconInLabel()) {
	    Icon icon = defaultAttributeIcon;
	    String iconName = ATTRIBUTE_ICON;
	    BoxLabel flabel = new BoxLabel(new IconShape(icon, iconName), text);
	    flabel.setDetachable(false);
	    flabel.setScalable(false);
	    flabel.setMovable(false);
	    attrBox.addShape(flabel);
	  } else {
	    attrBox.addShape(text);
	  }
	}
	attrBox.doLayout();
      } else if (attrBox instanceof BlockShape) {
	BlockShape block = (BlockShape) attrBox;
	block.removeAllStatements();
	SimpleStatementShape stmt;

	if (attributes != null && 
	    attributes.size() > 0) {
	  Iterator<Attribute> iter = attributes.iterator();
	  while (iter.hasNext()) {
	    Attribute f = iter.next();	    
	    if (template != null) { 
	      stmt = template.makeElementShape(SimpleStatementShape.ATTRIBUTE,
					       "Attribute", "attribute", 
					       ATTR_FG, ATTR_BG);
	      stmt.setText(f.toString(true, true));
	      stmt.setModel(f);
	      block.addShape(stmt);
	    }
	  }
	
	} else {
	  if (template != null) { 
	    stmt = template.makeElementShape(SimpleStatementShape.ATTRIBUTE,
					     "Attribute", "attribute", 
					     ATTR_FG, ATTR_BG);
	    stmt.setText(null);
	    block.addShape(stmt);
	  }
	}

	if (!block.isExpanded()) { 
	  block.expand();
	}
	boolean fit = block.isFitted();
	block.setFitted(true);
	block.doLayout();
	block.setFitted(fit);	
      }
      fitCompartments();
      doLayout();
    }
  }

  public void setEnumValues(List<Value> values) {
    if (isEnum() && 
	attrBox != null) {
      if (attrBox instanceof BoxLabel) { 
	TextShape text = null;
	attrBox.removeAllShapes();
	
	if (values != null && values.size() > 0) {
	  Iterator<Value> iter = values.iterator();
	  while (iter.hasNext()) {
	    Value v = iter.next();
	    String fontKey = ATTRIBUTE_FONT;
	    String colorKey = ATTRIBUTE_COLOR;
	    text = template.makeContentText(v.getName(), "value",
					    true, // multiline 
					    fontKey, colorKey);
	    text.setDisplayName("Value");
	    text.setModel(v);
	    attrBox.addShape(text);
	  }
	  if (attrBox instanceof BoxLabel) { 
	    BoxLabel box = (BoxLabel) attrBox; 
	    box.linkTextLines();
	  }
	} else {
	  // no value
	  text = template.makeContentText(null, "value", 
					  true, // multiline
					  ATTRIBUTE_FONT,
					  ATTRIBUTE_COLOR);
	  attrBox.addShape(text);
	}
	attrBox.doLayout();
      } else if (attrBox instanceof BlockShape) {
	BlockShape block = (BlockShape) attrBox;
	block.removeAllStatements();
	SimpleStatementShape stmt;

	if (values != null && values.size() > 0) {
	  Iterator<Value> iter = values.iterator();
	  while (iter.hasNext()) {
	    Value v = iter.next();
	    if (template != null) { 
	      stmt = template.makeElementShape(SimpleStatementShape.ENUMERATION,
					       "Enumeration", "enumeration", 
					       ENUM_FG, ENUM_BG);
	      stmt.setText(v.getName());
	      stmt.setModel(v);
	      block.addShape(stmt);
	    }
	  }
	} else {
	  if (template != null) { 
	    stmt = template.makeElementShape(SimpleStatementShape.ENUMERATION,
					     "Enumeration", "enumeration",
					     ENUM_FG, ENUM_BG);
	    stmt.setText(null);
	    block.addShape(stmt);
	  }
	}

	if (!block.isExpanded()) { 
	  block.expand();
	}
	boolean fit = block.isFitted();
	block.setFitted(true);
	block.doLayout();
	block.setFitted(fit);	
      }
      fitCompartments();
      doLayout();
    }
  }

  public void setOperations(List<Operation> operations) {
    if (operBox != null) {
      if (operBox instanceof BoxLabel) { 
	TextShape text = null;
	operBox.removeAllShapes();
	
	if (operations != null && 
	    operations.size() > 0) {
	  Iterator<Operation> iter = operations.iterator();
	  while (iter.hasNext()) {
	    Operation m = iter.next();
	    
	    String fontKey = OPERATION_FONT;
	    String colorKey = OPERATION_COLOR;
	    if (m.isStatic()) {
	      fontKey = STATIC_OPERATION_FONT;
	      colorKey = STATIC_OPERATION_COLOR;
	    } else if (m.isAbstract()) {
	      fontKey = ABSTRACT_OPERATION_FONT;
	      colorKey = ABSTRACT_OPERATION_COLOR;
	    }
	    text = template.makeContentText(m.toString(true), "operation", 
					    true, // multiline
					    fontKey, colorKey);
	    text.setDisplayName("Operation");
	    Icon icon = null;
	    String iconName = null;
	    Visibility v = m.getVisibility();
	    if (v != null && template.useIconInLabel()) {
	      switch (v) {
	      case Public:
		icon = publicOperationIcon;
		iconName = PUBLIC_OPERATION_ICON;
		break;
	      case Protected:
		icon = protectedOperationIcon;
		iconName = PROTECTED_OPERATION_ICON;
		break;
	      case Private:
		icon = privateOperationIcon;
		iconName = PRIVATE_OPERATION_ICON;
		break;
	      case Package:
	      default:
		icon = defaultOperationIcon;
		iconName = OPERATION_ICON;
		break;
	      }
	      BoxLabel mlabel = new BoxLabel(new IconShape(icon, iconName), text);
	      mlabel.setDetachable(false);
	      mlabel.setScalable(false);
	      mlabel.setMovable(false);
	      mlabel.setModel(m);
	      operBox.addShape(mlabel);
	    } else {
	      text.setModel(m);
	      operBox.addShape(text);
	    }
	  }
	  
	  if (operBox instanceof BoxLabel) { 
	    BoxLabel box = (BoxLabel) operBox; 
	    box.linkTextLines();
	  }
	} else {
	  // no operation
	  text = template.makeContentText(null, "operation", 
					  true, // multiline
					  OPERATION_FONT,
					  OPERATION_COLOR);
	  if (template.useIconInLabel()) {
	    Icon icon = defaultOperationIcon;
	    String iconName = OPERATION_ICON;
	    BoxLabel mlabel = new BoxLabel(new IconShape(icon, iconName), text);
	    mlabel.setDetachable(false);
	    mlabel.setScalable(false);
	    mlabel.setMovable(false);
	    operBox.addShape(mlabel);
	  } else {
	    operBox.addShape(text);
	  }
	}	
	operBox.doLayout();
      } else if (operBox instanceof BlockShape) {
	BlockShape block = (BlockShape) operBox;
	block.removeAllStatements();
	SimpleStatementShape stmt;
	if (operations != null && 
	    operations.size() > 0) {
	  Iterator<Operation> iter = operations.iterator();
	  while (iter.hasNext()) {
	    Operation m = iter.next();
	    if (template != null) { 
	      stmt = template.makeElementShape(SimpleStatementShape.METHOD_DECL,
					       "Operation", "operation", 
					       OPER_FG, OPER_BG);
	      stmt.setText(m.toString(true));
	      stmt.setModel(m);
	      block.addShape(stmt);
	    }
	  }
	} else { 
	  if (template != null) { 
	    stmt = template.makeElementShape(SimpleStatementShape.METHOD_DECL,
					     "Operation", "operation", 
					     OPER_FG, OPER_BG);
	    stmt.setText(null);
	    block.addShape(stmt);
	  }
	}

	if (!block.isExpanded()) { 
	  block.expand();
	}
	boolean fit = block.isFitted();
	block.setFitted(true);
	block.doLayout();
	block.setFitted(fit);	
      }
      fitCompartments();
      doLayout();
    }
  }

  public void initClassHeader(Label label, Label stereotype) {
    UMLUtil.initHeader(this, label, stereotype);
  }

  public void initAttributeOperation(DrawShape attr, DrawShape oper) {
    attrBox = attr;
    operBox = oper;
    hasAttribute = (attrBox != null);
    int n = getNumberOfHorizontalCompartments();
    if (attr != null) {
      attr.setDisplayName("Attributes");
      setShapeInCompartment(attr, 1);
      attrBox.setVisible(attrOperCompartmentShown);
    }
    if (oper != null) {
      oper.setDisplayName("Operations");
      setShapeInCompartment(oper, hasAttribute ? 2 : 1);
      operBox.setVisible(attrOperCompartmentShown);
    }

    doLayout();
  }

  public boolean isModernStyle() { 
    if (isEnum()) { 
      return (attrBox != null && attrBox instanceof BlockShape);
    } else { 
      return (attrBox != null && operBox != null && 
	      (attrBox instanceof BlockShape) && (operBox instanceof BlockShape));
    }
  }

  public void changeStyle(DrawShape attr, DrawShape oper) {
    if (isEnum()) { 
      if (attrBox != null && attr != null) {
	List<Value> values = getEnumValues(); 
	initAttributeOperation(attr, oper); 
	setEnumValues(values); 
      }
    } else { 
      if (attrBox != null && operBox != null && 
	  attr != null && oper != null) {
	List<Attribute> attributes = getAttributes();
	List<Operation> operations = getOperations();
	initAttributeOperation(attr, oper); 
	setAttributes(attributes);
	setOperations(operations);
      }
    }
  }

  public void showAttributeOperationCompartments() {
    int n = getNumberOfHorizontalCompartments();
    if (n == 1) {

      // necessary to sync with model, which may have been 
      // edited when the action box is hidden
      ModelElement model = getModel();
      if (model instanceof UMLClass) {
	UMLClass clsmodel = (UMLClass) model;
	List<Attribute> attributes = clsmodel.getAttributes();
	setAttributes(attributes);
	List<Operation> operations = clsmodel.getOperations();
	setOperations(operations);
      } else if (model instanceof Enumeration) {
	Enumeration enumModel = (Enumeration) model;
	List<Value> values = enumModel.getValues();
	setEnumValues(values);
      }

      float[] dividers;
      float hBefore = getShapeHeight();
      float h = 0, h0 = 0, h1 = 0, h2 = 0;
      h0 = hBefore; 
      if (attrBox != null ||
	  operBox != null) {

	if (attrBox != null) { 
	  attrBox.setVisible(true);
	  h1 = attrBox.getShapeHeight() + 2 * cpady;
	}
      
	if (operBox != null) {
	  operBox.setVisible(true);
	  h2 = operBox.getShapeHeight() + 2 * cpady;
	}
      
	h = (h0 + h1 + h2);
	if (attrBox != null && operBox != null) { 
	  dividers = new float[] { h0 / h, h1 / h };
	} else { 
	  dividers = new float[] { h0 / h };
	}
	moveEnd2(0, h1 + h2);
      } else {
	dividers = new float[] { 0.2f, 0.4f };
	moveEnd2(0, h0 * 4);
      }
      setHorizontalDividers(dividers);

      if (hasAttribute) {
	if (attrBox != null) { 
	  setShapeInCompartment(attrBox, 1);
	}
	if (operBox != null) { 
	  setShapeInCompartment(operBox, 2);
	}
      } else {
	if (operBox != null) { 
	  setShapeInCompartment(operBox, 1);
	}
      }

      arrangeSubshapes();
      fitCompartments();
      doLayout();
    }
    attrOperCompartmentShown = true;
  }

  public void hideAttributeOperationCompartments() {
    int n = getNumberOfHorizontalCompartments();
    if (n > 1) {
      if (attrBox != null) {
	attrBox.setVisible(false);
      }
      if (operBox != null) { 
	operBox.setVisible(false);
      }

      float h = getShapeHeight();
      float dy = 0;
      float[] dividers = getHorizontalDividers();
      if (dividers != null) {
	dy = -h * (1 - dividers[0]);
      }
      setHorizontalDividers(null);
      moveEnd2(0, dy);
      doLayout();
    }
    attrOperCompartmentShown = false;
  }

  public boolean isAttrOperCompartmentShown() {
    return attrOperCompartmentShown;
  }

  public boolean isAttributeCompartmentShown() {
    return attrOperCompartmentShown && (attrBox != null); 
  }

  public boolean isOperationCompartmentShow() {
    return attrOperCompartmentShown && (operBox != null); 
  }

  @Override
  public String toString() { 
    return getClassName();
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }

    attrs.put("isInterface", _interface);
    attrs.put("isAbstract", _abstract);
    attrs.put("isActive", _active);
    attrs.put("isMixin", _mixin);
    attrs.put("isEnum", _enum);
    attrs.put("isException", _exception);

    attrs.put("showAttribute", attrOperCompartmentShown);
    attrs.put("hasAttribute", attrBox != null);

    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("isInterface");
      if (s != null && s.length() > 0) { 
	_interface = Boolean.valueOf(s);
      }
      s = node.getAttribute("isAbstract");
      if (s != null && s.length() > 0) { 
	_abstract = Boolean.valueOf(s);
      }
      s = node.getAttribute("isActive");
      if (s != null && s.length() > 0) { 
	_active = Boolean.valueOf(s);
      }
      s = node.getAttribute("isMixin");
      if (s != null && s.length() > 0) { 
	_mixin = Boolean.valueOf(s);
      }
      s = node.getAttribute("isEnum");
      if (s != null && s.length() > 0) { 
	_enum = Boolean.valueOf(s);
      }
      s = node.getAttribute("isException");
      if (s != null && s.length() > 0) { 
	_exception = Boolean.valueOf(s);
      }
      setDrawAttr();

      s = node.getAttribute("showAttribute");
      attrOperCompartmentShown = Boolean.valueOf(s);
      s = node.getAttribute("hasAttribute");
      hasAttribute = true;
      if (s != null && s.length() > 0) { 
	hasAttribute = Boolean.valueOf(s);
      }
    }
  }

  public void arrangeSubshapes() {
    if (hasAttribute) {
      attrBox = getShapeInCompartment(1);
      operBox = getShapeInCompartment(2);
    } else { 
      attrBox = null;
      operBox = getShapeInCompartment(1);
    }
  }

  public boolean isInterface() {
    return _interface;
  }

  public void setInterface(boolean b) {
    this._interface = b;
    setDrawAttr();
  }

  public boolean isAbstract() {
    return _abstract;
  }

  public void setAbstract(boolean b) {
    this._abstract = b;
    setDrawAttr();
  }

  public boolean isActive() {
    return _active;
  }

  public void setActive(boolean b) {
    this._active = b;
    setShowVerticalGutter(b);
    setDrawAttr();
  }

  public boolean isMixin() {
    return _mixin;
  }

  public void setMixin(boolean b) {
    this._mixin = b;
    setDrawAttr();
  }

  public boolean isEnum() {
    return _enum;
  }

  public void setEnum(boolean b) {
    this._enum = b;
    setDrawAttr();
  }

  public boolean isException() {
    return _exception;
  }

  public void setException(boolean b) {
    this._exception = b;
    setDrawAttr();
  }

  @Override
  public void setDrawAttr(DrawAttr attr) {
    this.attr = attr;
    if (_interface) {
      attrInterface = attr; 
    } else if (_abstract) { 
      attrAbstractClass = attr; 
    } else if (_active) { 
      attrActiveClass = attr;
    } else if (_mixin) { 
      attrMixinClass = attr;
    } else if (_enum) { 
      attrEnum = attr;
    } else if (_exception) { 
      attrException = attr;
    } else { 
      attrClass = attr;
    }
  }

  public DrawAttr getDrawAttrClass() {
    return attrClass;
  }

  public void setDrawAttrClass(DrawAttr attr) {
    this.attrClass = attr;
  }

  public DrawAttr getDrawAttrActiveClass() {
    return attrActiveClass;
  }

  public void setDrawAttrActiveClass(DrawAttr attr) {
    this.attrActiveClass = attr;
  }

  public DrawAttr getDrawAttrMixinClass() {
    return attrMixinClass;
  }

  public void setDrawAttrMixinClass(DrawAttr attr) {
    this.attrMixinClass = attr;
  }

  public DrawAttr getDrawAttrAbstractClass() {
    return attrAbstractClass;
  }

  public void setDrawAttrAbstractClass(DrawAttr attr) {
    this.attrAbstractClass = attr;
  }

  public DrawAttr getDrawAttrInterface() {
    return attrInterface;
  }

  public void setDrawAttInterfacer(DrawAttr attr) {
    this.attrInterface = attr;
  }

  public DrawAttr getDrawAttrEnum() {
    return attrEnum;
  }

  public void setDrawAttrEnum(DrawAttr attr) {
    this.attrEnum = attr;
  }

  public DrawAttr getDrawAttrException() {
    return attrException;
  }

  public void setDrawAttrException(DrawAttr attr) {
    this.attrException = attr;
  }

  protected boolean _interface = false;
  protected boolean _abstract = false;
  protected boolean _active = false;
  protected boolean _mixin = false;

  protected boolean _enum = false;
  protected boolean _exception = false;

  protected boolean attrOperCompartmentShown = true;
  protected boolean hasAttribute = true;

  protected DrawShape attrBox;

  protected DrawShape operBox;

  protected DrawAttr attrClass;
  protected DrawAttr attrActiveClass;
  protected DrawAttr attrMixinClass;
  protected DrawAttr attrAbstractClass;
  protected DrawAttr attrInterface;
  protected DrawAttr attrEnum;
  protected DrawAttr attrException;

  public static void initStyle(Map iconMap, UMLClassDiagramTemplate template) {
    ClassShape.template = template;

    if (!styleInitialized) {
      if (iconMap != null) {
	classIcon = (Icon) iconMap.get(CLASS_ICON);
	absClassIcon = (Icon) iconMap.get(ABSTRACT_CLASS_ICON);
	interfaceIcon = (Icon) iconMap.get(INTERFACE_ICON);

	publicAttributeIcon = (Icon) iconMap.get(PUBLIC_ATTRIBUTE_ICON);
	protectedAttributeIcon = (Icon) iconMap.get(PROTECTED_ATTRIBUTE_ICON);
	privateAttributeIcon = (Icon) iconMap.get(PRIVATE_ATTRIBUTE_ICON);
	defaultAttributeIcon = (Icon) iconMap.get(ATTRIBUTE_ICON);

	publicOperationIcon = (Icon) iconMap.get(PUBLIC_OPERATION_ICON);
	protectedOperationIcon = (Icon) iconMap.get(PROTECTED_OPERATION_ICON);
	privateOperationIcon = (Icon) iconMap.get(PRIVATE_OPERATION_ICON);
	defaultOperationIcon = (Icon) iconMap.get(OPERATION_ICON);
      }
      if (template != null) {
	classNameFont = template.getFontAttr(CLASS_NAME_FONT);
	abstractClassNameFont = template.getFontAttr(ABSTRACT_CLASSNAME_FONT);
	interfaceNameFont = template.getFontAttr(INTERFACE_NAME_FONT);

	attributeFont = template.getFontAttr(ATTRIBUTE_FONT);
	staticAttributeFont = template.getFontAttr(STATIC_ATTRIBUTE_FONT);

	operationFont = template.getFontAttr(OPERATION_FONT);
	staticOperationFont = template.getFontAttr(STATIC_OPERATION_FONT);
	abstractOperationFont = template.getFontAttr(ABSTRACT_OPERATION_FONT);
      }
      styleInitialized = true;
    }
  }

  protected static boolean styleInitialized = false;

  protected static UMLClassDiagramTemplate template = null;

  protected static Icon classIcon;

  protected static Icon absClassIcon;

  protected static Icon interfaceIcon;

  protected static Icon publicAttributeIcon;

  protected static Icon protectedAttributeIcon;

  protected static Icon privateAttributeIcon;

  protected static Icon defaultAttributeIcon;

  protected static Icon publicOperationIcon;

  protected static Icon protectedOperationIcon;

  protected static Icon privateOperationIcon;

  protected static Icon defaultOperationIcon;

  protected static FontAttrEx classNameFont;

  protected static FontAttrEx abstractClassNameFont;

  protected static FontAttrEx interfaceNameFont;

  protected static FontAttrEx attributeFont;

  protected static FontAttrEx staticAttributeFont;

  protected static FontAttrEx operationFont;

  protected static FontAttrEx staticOperationFont;

  protected static FontAttrEx abstractOperationFont;

}
